Skip to content

improve error message in ridesx flashing#543

Merged
mangelajo merged 2 commits intojumpstarter-dev:mainfrom
bennyz:improve-ridesx-error
Apr 13, 2026
Merged

improve error message in ridesx flashing#543
mangelajo merged 2 commits intojumpstarter-dev:mainfrom
bennyz:improve-ridesx-error

Conversation

@bennyz
Copy link
Copy Markdown
Member

@bennyz bennyz commented Apr 12, 2026

fixes #540

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 12, 2026

Deploy Preview for jumpstarter-docs failed. Why did it fail? →

Name Link
🔨 Latest commit 9db6069
🔍 Latest deploy log https://app.netlify.com/projects/jumpstarter-docs/deploys/69db5c88fd8fd3000833d50e

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 12, 2026

Warning

Rate limit exceeded

@bennyz has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 33 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 10 minutes and 33 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0d5ba2b7-418f-4abc-9b7d-45d18fccce27

📥 Commits

Reviewing files that changed from the base of the PR and between 3128c4b and d8017f4.

📒 Files selected for processing (2)
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py
📝 Walkthrough

Walkthrough

Tightened OCI URL detection and validation: only oci:// is accepted for OCI images; inputs that look like partition:path now produce targeted CLI errors suggesting -t usage. Shared validation moved into the driver; tests expanded to cover these behaviors.

Changes

Cohort / File(s) Summary
Client
python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py
Removed broad OCI heuristics; require oci:// for OCI positional paths, added explicit click.ClickException hints when a partition:path or bare registry is provided without -t, and enforce OCI-positional requirement when -t mappings are present.
Driver
python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver.py
Added RideSXDriver._validate_oci_url(oci_url: str) static validator (enforces oci:// prefix and detects partition:path patterns) and replaced inline checks in flash_oci_image with this helper.
Tests — Client
python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py
Updated and added tests to expect oci://-only validation; new assertions that non-OCI or partition:path positional args raise click.ClickException with guidance about -t or oci://; added multi-target tests ensuring all -t specs are shown in errors and accepted OCI positional calls invoke flash_with_targets.
Tests — Driver
python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver_test.py
Added unit tests for _validate_oci_url, covering valid oci:// inputs, other schemes, bare registry strings, and various partition:path forms with expected hint text presence/absence.

Sequence Diagram(s)

(Skipped — changes are validation/UX improvements without a new multi-component control flow requiring visualization.)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • jumpstarter-dev/jumpstarter#790: Edits improving OCI vs partition:path parsing and clearer client-side error guidance when mixing -t with positional images.
  • Add tests for BLE driver #536: Related modifications to the RideSX client/driver flash handling and OCI URL/target validation.

Suggested labels

backport release-0.7

Suggested reviewers

  • mangelajo

Poem

🐰
I hop through colons, sniff each trail,
If images drift, I leave a trail:
“Add -t or oci:// — don’t be shy,”
I thump my foot and wink my eye,
Happy flashing beneath the sky.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: improving error messages for RideSX flashing scenarios.
Description check ✅ Passed The description references the linked issue #540 which is directly related to the changeset's error messaging improvements.
Linked Issues check ✅ Passed The PR implements the primary objective from #540 by adding clear error messages when mixing -t flags with standalone image paths and enforcing oci:// prefix requirements [#540].
Out of Scope Changes check ✅ Passed All changes focus on improving error handling and validation for RideSX flashing, directly addressing the requirements specified in #540 without introducing unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py`:
- Around line 423-429: The current guard only checks
self._looks_like_partition_spec(path) and misses non-OCI positional inputs
(e.g., boot_a:boot.img, ./boot_a.simg) that should trigger the "missing -t"
error in multi-target mode; update the condition in the argument-parsing code
around that block to raise the ClickException whenever the positional path is
not an OCI reference (use whatever project helper detects OCI refs, e.g., an
existing is_oci_reference/_is_oci_reference function or a small url/regex check)
OR self._looks_like_partition_spec(path) or the value resembles a local
filesystem image (contains '/' or '\' or ends with common image extensions or
os.path.exists(path)); reference self._looks_like_partition_spec and the
OCI-detection helper when making the change so non-OCI inputs are caught and the
same explanatory ClickException is raised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b77aa05a-ba91-41d6-b586-e27943c3b87e

📥 Commits

Reviewing files that changed from the base of the PR and between d7a65e0 and ab7546a.

📒 Files selected for processing (4)
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver_test.py

@bennyz bennyz force-pushed the improve-ridesx-error branch from ab7546a to 9db6069 Compare April 12, 2026 08:49
@bennyz bennyz requested a review from mangelajo April 12, 2026 08:59
Copy link
Copy Markdown
Contributor

@ambient-code ambient-code bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: improve error message in ridesx flashing

Fixes #540 — the approach is sound and test coverage is good. A few observations:

1. _looks_like_partition_spec misses bare filenames after colon

_looks_like_partition_spec("boot_a:boot.img") returns False because boot.img doesn't start with /, ./, ../, or ~. In flash() (single-file mode without target), this falls through to the generic error:

"This driver requires a target partition for non-OCI paths."

...which is less helpful than the new partition:path hint. The most common case (./) is covered, but bare filenames like boot_a:boot.img slip through.

Note: in _execute_flash_command (multi-target mode) this is handled correctly because the guard is not self._is_oci_path(path). The gap is only in flash().

2. Inconsistent exception types

  • flash() raises ValueError for partition:path input
  • _execute_flash_command() raises click.ClickException for similar input

click.ClickException formats cleanly to the terminal; ValueError will show as a raw traceback unless caught higher up. Since both are CLI-facing paths, they should probably both raise click.ClickException.

3. Example in multi-target error only shows one target spec

f"  j storage flash -t {path} -t {next(iter(target_specs))}"

If the user has 3+ -t specs the example only shows one, which may confuse them. Consider joining all specs:

t_flags = " ".join(f"-t {s}" for s in target_specs)
f"  j storage flash -t {path} {t_flags}"

4. Duplicated path-detection logic in driver vs client

_validate_oci_url (driver) manually re-implements the after_colon.startswith(...) check that _looks_like_partition_spec (client) also does. Architecturally acceptable given they're separate classes, but worth a comment noting the intentional duplication.

5. _is_oci_path passes host:port/image-style refs

A URL like localhost:5000/myimage passes _is_oci_path":" in path, "/" in path, doesn't start with excluded prefixes, and after_colon (5000/myimage) doesn't start with a path char. Probably fine in practice, but worth noting as a known edge case.


Overall: the core fix is correct and the main scenarios from #540 are addressed. Items 2 and 3 above are the most worth fixing before merge.

_, after_colon = path.split(":", 1)
if after_colon.startswith(("/", "./", "../", "~")):
return False
return True
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edge case: host:port/image passes as OCI

A URL like localhost:5000/myimage would pass this check — ":" in path, "/" in path, doesn't start with any excluded prefix, and after_colon (5000/myimage) doesn't start with a path character. Worth documenting as a known limitation, or guard with after_colon.isdigit() to reject host:port patterns.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that's intended, seems like a container image path.

# Traditional single file mode
if target is None:
# Detect partition:path patterns and give a specific hint
if isinstance(path, str) and self._looks_like_partition_spec(path):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_looks_like_partition_spec misses bare filenames after the colon

_looks_like_partition_spec("boot_a:boot.img") returns False because boot.img doesn't start with /, ./, ../, or ~. So flash("boot_a:boot.img") (without target) falls through to the generic error below instead of hitting this helpful hint. Consider also matching values where after_colon has no path separator at all but looks like a filename (e.g. ends with .img, .simg, .bin).

# Detect partition:path patterns and give a specific hint
if isinstance(path, str) and self._looks_like_partition_spec(path):
partition, filepath = path.split(":", 1)
raise ValueError(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent exception type: ValueError vs click.ClickException

This raises ValueError, but the analogous guard in _execute_flash_command raises click.ClickException, which formats cleanly in the terminal without a traceback. A ValueError here will produce a raw traceback unless caught higher up. Suggest using click.ClickException here too for consistent UX.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, exceptions for flash() are not only meant for CLI.

if ":" not in value:
return False
_, after_colon = value.split(":", 1)
return after_colon.startswith(("/", "./", "../", "~"))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional duplication — worth a comment

The after_colon.startswith(...) logic here is mirrored in driver.py's _validate_oci_url. A brief note explaining the duplication is intentional (client and driver are separate classes) would help future maintainers remember to update both if the set of path prefixes ever changes.

f"'{path}' is not an OCI reference and is missing the -t flag.\n"
f"Each image must be passed as partition:path with its own -t.\n\n"
f"Example:\n"
f" j storage flash -t {path} -t {next(iter(target_specs))}"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example only shows one of the target specs

If the user has 3+ -t specs, only one appears in the suggestion, which may leave them confused. Consider including all:

t_flags = " ".join(f"-t {s}" for s in target_specs)
f"  j storage flash -t {path} {t_flags}"

hint = ""
if ":" in oci_url:
_, after = oci_url.split(":", 1)
if after.startswith(("/", "./", "../", "~")):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated path-detection logic

This after.startswith(("/", "./", "../", "~")) check mirrors _looks_like_partition_spec in client.py. Since these live in separate classes (client vs driver) the duplication is acceptable, but a comment here noting the parallel would help keep them in sync if the set of path prefixes changes.

@bennyz
Copy link
Copy Markdown
Member Author

bennyz commented Apr 13, 2026

I changed the approach, looks like this now:

jumpstarter⚡qti-snapdragon-ride4-sa8775p-03➤ uv run j storage flash -t system_a:./system_a.simg boot_a:./boot_a.simg
Error: 'boot_a:./boot_a.simg' is missing the -t flag.
Each partition mapping needs its own -t flag.

Example:
  j storage flash -t boot_a:./boot_a.simg -t system_a:./system_a.simg

For OCI images, use the oci:// prefix:
  j storage flash oci://registry.com/image:tag -t system_a:./system_a.simg

jumpstarter⚡qti-snapdragon-ride4-sa8775p-03➤ uv run j storage flash quay.io/bzlotnik/ridesx-image:ai
Error: OCI URLs must start with oci://, got: quay.io/bzlotnik/ridesx-image:ai
Usage: j storage flash oci://quay.io/bzlotnik/ridesx-image:ai

Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
Assisted-by: claude-opus-4.6
@bennyz bennyz force-pushed the improve-ridesx-error branch from 715863f to 3128c4b Compare April 13, 2026 09:20
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py (1)

97-133: Consider parameterizing the repeated invalid-input matrix.

The same assertion pattern is repeated across multiple inputs in Line 99–Line 132; a parameterized test would keep this easier to extend.

Refactor sketch
+@pytest.mark.parametrize(
+    ("image", "targets"),
+    [
+        ("boot_a:/path/to/boot.img", ("system_a:/path/to/system.img",)),
+        ("boot_a:./boot_a.simg", ("system_a:./system_a.simg",)),
+        ("boot_a:boot.img", ("system_a:/path/to/system.img",)),
+        ("./boot_a.simg", ("system_a:/path/to/system.img",)),
+        ("quay.io/org/image:tag", ("boot_a:boot.img",)),
+    ],
+)
+def test_execute_flash_command_rejects_non_oci_positional_with_targets(
+    ridesx_client, image, targets
+):
+    with pytest.raises(click.ClickException, match="missing the -t flag"):
+        ridesx_client._execute_flash_command(image, targets)
-
-def test_execute_flash_command_rejects_non_oci_positional_with_targets(ridesx_client):
-    ...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py`
around lines 97 - 133, The test
test_execute_flash_command_rejects_non_oci_positional_with_targets repeats the
same pytest.raises assertion for multiple invalid inputs; refactor it to a
single parametric test using pytest.mark.parametrize that iterates over the
invalid positional values and calls ridesx_client._execute_flash_command with
each value and a representative second-target tuple, asserting a
click.ClickException with the "missing the -t flag" match; keep the same test
name (or rename clearly) and include the original cases (e.g.,
"boot_a:/path/to/boot.img", "boot_a:./boot_a.simg", "boot_a:boot.img",
"./boot_a.simg", "quay.io/org/image:tag") as the parameter list.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py`:
- Around line 97-133: The test
test_execute_flash_command_rejects_non_oci_positional_with_targets repeats the
same pytest.raises assertion for multiple invalid inputs; refactor it to a
single parametric test using pytest.mark.parametrize that iterates over the
invalid positional values and calls ridesx_client._execute_flash_command with
each value and a representative second-target tuple, asserting a
click.ClickException with the "missing the -t flag" match; keep the same test
name (or rename clearly) and include the original cases (e.g.,
"boot_a:/path/to/boot.img", "boot_a:./boot_a.simg", "boot_a:boot.img",
"./boot_a.simg", "quay.io/org/image:tag") as the parameter list.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 46079594-2993-4202-943d-ebc57ec26466

📥 Commits

Reviewing files that changed from the base of the PR and between 715863f and 3128c4b.

📒 Files selected for processing (4)
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client_test.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver_test.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/driver_test.py
  • python/packages/jumpstarter-driver-ridesx/jumpstarter_driver_ridesx/client.py

Remove the _is_oci_path heuristic and _looks_like_partition_spec
methods. OCI references now require the oci:// prefix, matching
what FLS fastboot expects downstream.

Improve error messages to distinguish between missing -t flags
and missing oci:// prefixes based on path structure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Benny Zlotnik <bzlotnik@redhat.com>
@bennyz bennyz force-pushed the improve-ridesx-error branch from 3128c4b to d8017f4 Compare April 13, 2026 09:34
@mangelajo mangelajo enabled auto-merge (squash) April 13, 2026 09:37
@mangelajo mangelajo merged commit 46de464 into jumpstarter-dev:main Apr 13, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

j storage flash calls can be confusing with multiple targets

2 participants